/*!
 * \file build_sym_table_visitor.cc
 *
 * \brief Implementation of the BuildSymTableVisitor
 *
 * \date Started: 6/8/2010
 * \date Last Modified: 6/28/2010
 * \authors Alan LaMielle, Nicholas Jeanette
 *
 * Copyright (c) 2010, Colorado State University <br>
 * All rights reserved. <br>
 * See ../../COPYING for details. <br>
 */

#include <iegenlib/iegenlib.h>

#include <iostream>
#include <algorithm>
#include <sstream>

using std::find;
using std::stringstream;

namespace iegenlib{

BuildSymTableVisitor::BuildSymTableVisitor(set<string> symbolics)
{
   FunCounter = 0;
   tupvarlist = list<string>();
   exists = set<string>();
   syms = set<string>(symbolics.begin(), symbolics.end());
   mNumToDups = map<string,list<string> >();
   currentNames = set<string>(symbolics.begin(), symbolics.end());
   iArity = false;
   inArity = outArity = 0;
}

/*
   Puts variables in tupvarMap with an initial count of 0.
   Puts each var into the tupvarlist paired with its position in the tuple.
   Constants get put into the constVals list paired with its position in
   the tuple.
*/
void BuildSymTableVisitor::VarTupleCase(VarTuple* node)
{
 // if(firstConj){
   for(list<VarExp*>::const_iterator itr = node->begin();
         itr!= node->end();
            ++itr)
   {
      string tmpId((*itr)->getId());
      if(tmpId.compare("")==0)
      {//the tuple var is a constval
         stringstream s;
         s << (*itr)->getCoeff();
         tmpId = s.str();
         TupleList.push_back(TupleType((*itr)->getCoeff()));
      }
      else
      {//the tuple var is a regular tupvar
         //insert the name into the tupvarlist
         TupleList.push_back(TupleType(tmpId));
         tupvarlist.push_back(tmpId);
      }
   }

   //used for relations to know the split
   if(!iArity){
      inArity = (node->getVars()).size();
      iArity = true;
   }else{
      outArity = (node->getVars()).size();
   }
//  }
}

void BuildSymTableVisitor::FuncExpOut(FuncExp* node)
{
   ++FunCounter;
}

void BuildSymTableVisitor::VarExpOut(VarExp* node)
{
//checks that its not in the tupvarlist
   for(list<string>::const_iterator itr = tupvarlist.begin();
         itr != tupvarlist.end();
            ++itr)
   {
      if(itr->compare(node->getId())==0)
      {
         //this item exists in the tupVars
         return;
      }
   }
//checks that its not in the symbolics list
   for(set<string>::const_iterator itr = syms.begin();
         itr != syms.end();
            ++itr)
   {
      if(itr->compare(node->getId())==0)
      {
         //this item exists in the tupVars
         return;
      }
   }
   if(node->getId().size() != 0){
      exists.insert(node->getId());
      currentNames.insert(node->getId());
   }
}

void BuildSymTableVisitor::buildSymTable()
{
// This loop is to place the values from the tupvarlist into the set of unique
// names (tups) it must be done this way as we need to compare the pairs first
// this loop makes a call to alreadyInSet as that was created to check all sets
// but in this case we are just checking that the name doesn't already exist in
// the set called tups.
   set<string> tups;
   int index = 0;
   for(list<string>::const_iterator itr = tupvarlist.begin();
         itr != tupvarlist.end();
            ++itr)
   {
      //Make sure that the index is pointing at the correct location in TupleList
      while(TupleList.at(index).isConst()) ++index;

      if(!alreadyInSets(*itr))
      {
         //If the variable is not in any of the three sets, insert it into the
         //tups and into the currentNames sets
         tups.insert(*itr);
         currentNames.insert(*itr);
      }else{
         //If the variable is in any of the three sets, then create a new name
         //for the variable, update the TupleType entry, and then insert it into
         //tups and currentNames sets
         string name = iegenlib::createUniqueName(*itr, currentNames);
         currentNames.insert(name);
         if(name.compare(*itr) != 0){
            mNumToDups[*itr].push_back(name);
         }
         tups.insert(name);
         TupleList[index] = TupleType(name);
      }
      //move onto the next TupleType
      ++index;
   }

   ST = new SymbolTable( &tups, &syms, &exists, FunCounter);
   makeEqualities();
}

void BuildSymTableVisitor::PresSetIn(PresSet* node)
{
     throw StringException("can not use visitor with set of multiple conjunctions");
}

void BuildSymTableVisitor::PresRelationIn(PresRelation* node)
{
     throw StringException("can not use visitor with relation of multiple conjunctions");
}

void BuildSymTableVisitor::ConjunctionOut(Conjunction* node)
{
  buildSymTable();
}

SymbolTable* BuildSymTableVisitor::getSymTable()
{
   return ST;
}

bool BuildSymTableVisitor::alreadyInSets(string newName)
{
//check that it is not already defined in the list of used tuples and existentials and symbolics
   if(currentNames.find(newName) != currentNames.end()){
      return true;
   }
   ///newName is truly a newName so we return false
   return false;
}


list<string> BuildSymTableVisitor::getTupleVarList(){
   return tupvarlist;
}

void BuildSymTableVisitor::makeEqualities()
{

   for(map<std::string,list<std::string> >::iterator it = mNumToDups.begin();
         it != mNumToDups.end(); ++it){
      list<std::string> countList = it->second;
      for(list<std::string>::iterator lit = countList.begin();
         lit != countList.end();
            ++lit)
      {
           //need to get expvec for the nodes
           ExpVec orig = ST->constructExpVec(it->first,1);
           ExpVec dup = ST->constructExpVec((*lit),-1);
           ExpVec sum = orig+dup;

           equalities.insert(SREquality(sum));
      }
   }
}

}//end namespace iegenlib
